package exam;

import java.io.*;
import java.awt.*;
import java.awt.event.*;

import javax.swing.*;

import java.net.*;

public class TCPServer implements Runnable {
	// Connect status constants
	public final static int NULL = 0;
	public final static int DISCONNECTED = 1;
	public final static int DISCONNECTING = 2;
	public final static int BEGIN_CONNECT = 3;
	public final static int CONNECTED = 4;
	// Other constants
	public final static TCPServer tcpObj = new TCPServer();
	public final static String END_CHAT_SESSION = String.valueOf(new Character((char) 0));
	// Indicates the end of a session
	
	public final static String statusMessages[] = {
		" Error! Could not connect!", " Disconnected", " Disconnecting...",
		" Connecting...", " Connected" };

	// Connection state info
	public static String serverIP = "localhost";
	public static int port = 1234;
	public static int connectionStatus = DISCONNECTED;
	public static String statusString = statusMessages[connectionStatus];

	// Various GUI components and info
	public static JTextField portField = null;
	public static JButton connectButton = null;
	public static JButton disconnectButton = null;
	public static JButton clearButton = null;

	// TCP Components
	public static ServerSocket svrsocket;
	public static Socket socket;
	public static BufferedReader in;
	public static PrintWriter out = null;
	public static File file = null;
	public static FileReader fr = null;
	public static StringBuffer sendString = new StringBuffer("");

	// ///////////////////////////////////////////////////////////////

	// Cleanup for disconnect
	private static void cleanUp() {
		try {
			if (svrsocket != null) {
				svrsocket.close();
				svrsocket = null;
			}
		} catch (IOException e) {
			svrsocket = null;
		}

		try {
			if (socket != null) {
				socket.close();
				socket = null;
			}
		} catch (IOException e) {
			socket = null;
		}

		try {
			if (in != null) {
				in.close();
				in = null;
			}
		} catch (IOException e) {
			in = null;
		}

		try {
			if (fr != null) {
				fr.close();
				fr = null;
			}
		} catch (IOException e) {
			fr = null;
		}

		if (out != null) {
			out = null;
		}
	}

	// ///////////////////////////////////////////////////////////////

	// Checks the current state and sets the enables/disables
	// accordingly
	public void run() {
		switch (connectionStatus) {
		case DISCONNECTED:
			connectButton.setEnabled(true);
			disconnectButton.setEnabled(false);
			portField.setEnabled(true);
			break;

		case DISCONNECTING:
			connectButton.setEnabled(false);
			disconnectButton.setEnabled(false);
			portField.setEnabled(false);
			break;

		case CONNECTED:
			connectButton.setEnabled(false);
			disconnectButton.setEnabled(true);
			portField.setEnabled(false);
			break;

		case BEGIN_CONNECT:
			connectButton.setEnabled(false);
			disconnectButton.setEnabled(false);
			portField.setEnabled(false);
			break;
		}

		// Make sure that the button/text field states are consistent
		// with the internal states
		portField.setText(String.valueOf((new Integer(port))));
	}

	// /////////////////////////////////////////////////////////////////////

	public JPanel OptionsPane() {
		JPanel pane = null;
		ActionListener buttonListener = null;

		// Create an options pane
		JPanel optionsPane = new JPanel(new BorderLayout(10, 20));

		// Port input
		pane = new JPanel(new FlowLayout(FlowLayout.LEFT));
		pane.add(new JLabel("Port : "));
		portField = new JTextField(10);
		portField.setEditable(true);
		portField.setText(String.valueOf((new Integer(port))));
		portField.addFocusListener(new FocusAdapter() {
			public void focusLost(FocusEvent e) {
				// should be editable only when disconnected
				if (connectionStatus != DISCONNECTED) {
					tcpObj.run();
				} else {
					int temp;
					try {
						temp = Integer.parseInt(portField.getText());
						port = temp;
					} catch (NumberFormatException nfe) {
						portField.setText(String.valueOf((new Integer(port))));
					}
				}
			}
		});
		pane.add(portField);
		optionsPane.add(pane, "North");

		// Connect/Disconnect buttons
		JPanel buttonPane = new JPanel(new GridLayout(1, 2));
		buttonListener = new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				// Request a connection initiation
				if (e.getActionCommand().equals("Connect")) {
					try {
						svrsocket = new ServerSocket(port);
						socket = svrsocket.accept();
						connectionStatus = CONNECTED;
						tcpObj.run();
//						in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//						out = new PrintWriter(socket.getOutputStream());
					} catch (IOException e1) {
						// TODO Auto-generated catch block
						e1.printStackTrace();
					}
				}
				// Disconnect
				else {
//					out.write(END_CHAT_SESSION);
//					out.flush();

					// Clean up (close all streams/sockets)
					cleanUp();
					connectionStatus = DISCONNECTED;

					tcpObj.run();
				}
			}
		};
		connectButton = new JButton("Connect");
		connectButton.setMnemonic(KeyEvent.VK_C);
		connectButton.setActionCommand("Connect");
		connectButton.addActionListener(buttonListener);
		connectButton.setEnabled(true);
		disconnectButton = new JButton("Disconnect");
		disconnectButton.setMnemonic(KeyEvent.VK_D);
		disconnectButton.setActionCommand("Disconnect");
		disconnectButton.addActionListener(buttonListener);
		disconnectButton.setEnabled(false);
		buttonPane.add(connectButton);
		buttonPane.add(disconnectButton);
		optionsPane.add(buttonPane, "South");

		return optionsPane;
	}

	public void settingRun() {
		String s = "";
		
		if (connectionStatus == CONNECTED) {
			while (true) {
				try {
					in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
					out = new PrintWriter(socket.getOutputStream());
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				
				try { // Poll every ~1 ms
					Thread.sleep(1);
				} catch (InterruptedException e) {
				}

				try {
					// Send data
					if (sendString.length() != 0) {
						out.println(String.valueOf(sendString));
						out.flush();
						sendString.setLength(0);
						SwingUtilities.invokeLater(tcpObj);
					}

					// Receive data
					if (in.ready()) {
						s = String.valueOf(in.read());
						
						if ((s != null) && (s.length() != 0)) {
							// Check if it is the end of a transmission
							if (s.equals(END_CHAT_SESSION)) {
								connectionStatus = DISCONNECTED;
								SwingUtilities.invokeLater(tcpObj);
							}

							// Otherwise, receive what text
							else {
								ChatGUI.chatReceive_Ascii.append(s + "\n");
								ChatGUI.chatReceive_Hex.append(s + "\n");
								ChatGUI.chatReceive_Decimal.append(s + "\n");
								SwingUtilities.invokeLater(tcpObj);
					}
						}
					}
				} catch (IOException e) {
					cleanUp();
					connectionStatus = DISCONNECTED;
					SwingUtilities.invokeLater(tcpObj);
				}
			}
		}
	}
	
/*	private static void changeStatusTS(int newConnectStatus, boolean noError) {
		// Change state if valid state
		if (newConnectStatus != NULL) {
			connectionStatus = newConnectStatus;
		}

		// If there is no error, display the appropriate status message
		if (noError) {
			statusString = statusMessages[connectionStatus];
		}
		// Otherwise, display error message
		else {
			statusString = statusMessages[NULL];
		}

		// Call the run() routine (Runnable interface) on the
		// error-handling and GUI-update thread
		SwingUtilities.invokeLater(tcpObj);
	}
	
	private static void changeStatusNTS(int newConnectStatus, boolean noError) {
		// Change state if valid state
		if (newConnectStatus != NULL) {
			connectionStatus = newConnectStatus;
		}

		// If there is no error, display the appropriate status message
		if (noError) {
			statusString = statusMessages[connectionStatus];
		}
		// Otherwise, display error message
		else {
			statusString = statusMessages[NULL];
		}

		// Call the run() routine (Runnable interface) on the
		// current thread
		tcpObj.run();
	}*/
	
	public static void sendString(StringBuffer s){
		sendString = s;
	}
	
	// The main
	public static void main(String[] args) {
		new TCPServer();
	}
}